package com.zrc.superframe_net.common.download

import android.annotation.SuppressLint
import com.blankj.utilcode.util.FileUtils
import com.zrc.superframe_net.common.net.Net.Companion.client
import com.zrc.superframe_net.common.net.Net.Companion.downService
import com.zrc.superframe_net.common.net.Net.Companion.log
import com.zrc.superframe_net.common.net.RetryWhenNetwork
import com.zrc.superframe_net.room.NetRoomUtil
import com.zrc.superframe_net.room.download.DownloadInfo
import com.zrc.superframe_net.room.download.DownloadState
import com.zrc.superframe_net.utils.NetUtils
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import java.io.File

/**
 * @Author: superman
 * @CreateTime: 6/27/21
 * @Describe:下载工具类
 */
class NetDownloadManager(
    private val downCallBack: NetDownloadCallBack //下载状态发生改变时，回调
) : NetDownloadProgressCallBack, NetDownloadCallBack {

    init {//初始化
        client().addNetProgressListener(this)//监听
        log("NetDownloadManager -> init") //输出日志
    }

    private var downQueue = mutableSetOf<DownloadInfo>() //队列

    //暂停下载(downInfo:操作的对象类,isNextDownload:暂停后，是否检测开启下一个队列)
    private fun pause(downInfo: DownloadInfo, isNextDownload: Boolean) {
        downInfo.observer?.dispose() //等待中的时候，它是没有观察者的
        updateState(downInfo, DownloadState.PAUSE, isNextDownload) //刷新状态
        log("暂停：并保存数据")//输出日志
    }

    //开始下载（downInfo:操作的类）
    private fun start(downInfo: DownloadInfo) {
        if (client().currentDownloadCount >= client().mBuilder.maxDownloadCount) {//队列已满，进入等待状态
            log("队列已满，进入等待.")
            updateState(downInfo, DownloadState.WAIT)
            return
        }
        if (!downQueue.contains(downInfo)) {
            downQueue.add(downInfo)
        }
        client().addCurrentDownloadCount()//新增队列
        downService //开始下载
            .download("bytes=" + downInfo.readLength + "-", downInfo.url)
            .subscribeOn(Schedulers.io())
            .unsubscribeOn(Schedulers.io())
            .retryWhen(RetryWhenNetwork())
            .map {
                NetUtils.instance.writeCache(it, File(downInfo.savePath), downInfo)
                downInfo
            }
            .observeOn(AndroidSchedulers.mainThread(), true)
            .subscribe(downInfo.observer!!)
        log(
            """
            开始下载：
            url:${downInfo.url}
            star:${downInfo.readLength}
            """
        )//输出log
    }

    //让一个等待的 队列先开始，暂停掉某一条
    private fun wait(downInfo: DownloadInfo) {
        if (client().currentDownloadCount >= client().mBuilder.maxDownloadCount) {
            val iterator = downQueue.iterator()
            while (iterator.hasNext()) {
                val next = iterator.next()
                if (downInfo.getState() == DownloadState.DOWN) {
                    pause(next, false)
                    updateState(next, DownloadState.WAIT, false)
                    break
                }
            }
        }
        start(downInfo)
    }

    //下载
    fun down(downInfo: DownloadInfo) {
        when (downInfo.getState()) {
            DownloadState.NORMAL -> start(downInfo)
            DownloadState.WAIT -> wait(downInfo)
            DownloadState.DOWN -> pause(downInfo, true)
            DownloadState.PAUSE, DownloadState.STOP -> start(downInfo)
            DownloadState.ERROR -> { //失败重新下载
                downInfo.readLength = 0
                downInfo.countLength = 0
                start(downInfo)
            }
            DownloadState.FINISH -> {//安装apk
                if (downInfo.url.contains("apk")) {
                    val path = downInfo.savePath
                    NetUtils.instance.installApk(path)
                }
            }
        }
    }

    //更新进度
    override fun update(url: String, readCount: Long, totalCount: Long, done: Boolean) {
        var readCount = readCount
        val downInfo = queryDownInfoBy(url)
        if (downInfo != null) {
            val pretProgress =
                (downInfo.readLength * 1.0 / downInfo.countLength * 100).toInt() //之前的进度
            if (downInfo.countLength > totalCount) {//保存 readCount/setCountLength
                readCount += downInfo.countLength - totalCount
            } else {
                downInfo.countLength = totalCount
            }
            downInfo.readLength = readCount
            val progress = downInfo.getProgress()
            if (pretProgress != progress) {//进度如果没有改变。就不通知UI刷新了
                updateProgress(downInfo, progress) //刷新进度
            }
        }
    }

    //根据URL 查找下载对象
    private fun queryDownInfoBy(url: String): DownloadInfo? {
        val iterator = downQueue.iterator()
        while (iterator.hasNext()) {
            val next = iterator.next()
            if (next.url == url) {
                return next
            }
        }
        return null
    }

    //根据配置创建一个DownInfo
    @JvmOverloads
    fun createDownInfo(
        url: String,
        savePath: String? = null,
        fileName: String? = null,
        notNeedCache: Boolean = false
    ): DownloadInfo {
        var uri = savePath ?: ""
        if (savePath.isNullOrEmpty()) {
            uri = if (notNeedCache){
                NetUtils.instance.mContext.cacheDir.absolutePath
            }else{
                NetUtils.instance.downloadDirectory().absolutePath
            }
        }
        var downInfo = NetRoomUtil.database.downInfoDao().find(url)
        if (downInfo != null) { //文件被删除
            val file = File(downInfo.savePath)
            if (!file.exists()) {
                NetRoomUtil.database.downInfoDao().delete(downInfo)
                downInfo = null
            }
        }
        if (downInfo == null) { //正常流程
            downInfo = DownloadInfo()
            downInfo.notNeedCache=notNeedCache
            downInfo.url = url
            if (fileName.isNullOrEmpty()) {
                val f = File(url)
                val kuozhanming = FileUtils.getFileExtension(f)
                downInfo.savePath = "${uri}/${DownloadInfo.getHexString(url)}.${kuozhanming}"
            } else {
                downInfo.savePath = "$uri/$fileName"
            }
        }
        if (downInfo.getState() == DownloadState.DOWN) {//应用被强关的时候
            downInfo.downState = DownloadState.PAUSE.state
        }
        downInfo.callBack = this //设置监听
        downInfo.observer = NetDownloadObserver(downInfo, this) //观察者
        return downInfo
    }

    //界面退出的时候，记得销毁对象，不销毁。后台会静默下载
    fun destory() {
        client().removeNetProgressListener(this)
        //暂停
        val iterator = downQueue.iterator()
        while (iterator.hasNext()) {
            val next = iterator.next()
            if (next.getState() == DownloadState.DOWN) {
                pause(next, false)
            }
        }
        downQueue.clear()
        log("NetDownloadManager -> gc")
    }

    //进度发生改变的时候
    @SuppressLint("CheckResult")
    override fun updateProgress(info: DownloadInfo, progress: Int) {
        log("updateProgress -> $progress %")//输出日志
        if (!info.notNeedCache){
            NetRoomUtil.database.downInfoDao().updateWithInsert(info) //更新数据库
        }
        Observable.just(info)// 回调 主线程
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { downCallBack.updateProgress(it, progress) }
    }

    //下载状态发生改变的时候
    override fun updateState(info: DownloadInfo, state: DownloadState) {
        updateState(info, state, true)
    }

    //下载状态发生改变的时候(info:修改的对象,state:新的状态,isNextDownload:是否开启下一个队列 )
    @SuppressLint("CheckResult")
    fun updateState(info: DownloadInfo, state: DownloadState, isNextDownload: Boolean) {
        log(String.format("下载地址:%s\n状态：%d", info.url, state.state))
        info.downState = state.state
        if (!info.notNeedCache){
            NetRoomUtil.database.downInfoDao().updateWithInsert(info) //更新下载的数据
        }
        when (state) {
            DownloadState.PAUSE, DownloadState.STOP, DownloadState.ERROR, DownloadState.FINISH -> {
                client().subCurrentDownloadCount() //删除队列
                if (isNextDownload) {//下一个任务
                    nextDownload()
                }
            }
            else -> {
            }
        }
        Observable.just(info) // 回调 主线程
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe { downCallBack.updateState(it, state) }
    }

    //查找 为 WAIT 状态的队列 并开启他，只开一个
    private fun nextDownload() {
        downQueue.forEach {
            when (it.getState()) {
                DownloadState.WAIT -> {//如果处于等待。并且队列没满
                    if (client().currentDownloadCount < client().mBuilder.maxDownloadCount) {
                        start(it)
                    } else {
                        return@forEach
                    }
                }
                else -> {
                    return@forEach
                }
            }
        }
    }

    //开始全部下载,一般用户app重新打开时 开启默认下载
    fun downloadAll(downloadInfos: List<DownloadInfo>?=null) {
        downloadInfos?.forEach {
            if (!downQueue.contains(it)) {
                downQueue.add(it)
            }
        }
        downQueue.forEach {
            when (it.getState()) {
                DownloadState.WAIT, DownloadState.PAUSE, DownloadState.STOP, DownloadState.ERROR -> {//四种状态下，自动重启队列 WAIT（等待） PAUSE（暂停） STOP（停止） ERROR（错误）
                    if (client().currentDownloadCount < client().mBuilder.maxDownloadCount) { //如果处于等待。并且队列没满
                        down(it)
                    } else {
                        return@forEach
                    }
                }
                else -> {
                    return@forEach
                }
            }
        }
    }
}